#ifndef _PROTOCOL_HPP
#define _PROTOCOL_HPP

#include <iostream>
#include <sstream>
#include <unordered_map>
#include <string>
#include <vector>
#include <algorithm>
#include <cassert>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sendfile.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "Util.hpp"
#include "Log.hpp"

// 可以写入配置文件中
#define WWW_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"
#define PAGE_404 "/404.html"
#define PAGE_401 "401.html"

#define OK 200
#define NOT_FOUND 404
#define BAD_REQUEST 401

std::string Code2Desc(int code)
{
    std::string desc;
    switch(code)
    {
        case 200:
            desc = "OK";
            break;
        case 404:
            desc = "NOT_FOUND";
            break;
        default:
            // other
            break;
    }

    return desc;
}

static std::string Suffix2Desc(std::string suffix)
{
    static std::unordered_map<std::string, std::string> suffix2desc = {
      {".xml", "text/xml"}, 
      {".css", "text/css"},
      {".html", "text/html"}, 
      {".jpg", "image/jpeg"}, 
      {".png", "image/png"},
      {".gif", "image/gif"},
      {".ttf", "application/x-font-ttf"},
      {".json", "application/json"}, 
      {".js", "application/javascript"}};
    
    std::unordered_map<std::string, std::string>::iterator iter = suffix2desc.find(suffix);
    if(iter != suffix2desc.end())
    {
        // 找到格式
        return iter->second;
    }
    else 
    {
        // 没有找到
        // 默认为.html
        LOG(WARNING, suffix + " not find right style!" );
        return suffix2desc[".html"];
    }
}

class HttpRequest
{
    public:
        std::string request_line;
        std::vector<std::string> request_header;
        std::string blank;
        std::string request_body;

        // 解析部分
        std::string method;
        std::string uri;
        std::string version;

        // 存储header
        std::unordered_map<std::string, std::string> header_kv;

        // Content-Length
        int content_length;
        // Content-Type
        std::string content_type;
        // 后缀
        std::string suffix;
        int size;

        // 分割URI
        std::string path;
        std::string query_string;

        // 判断是否需要进行CGI处理
        bool cgi;

    public:
        HttpRequest() :content_length(0), cgi(false){}
        ~HttpRequest(){}
};

class HttpResponse
{
    public:
        std::string response_line;
        std::vector<std::string> response_header;
        std::string blank;
        std::string response_body;

        // 状态码
        int status_code;

        // 网页文件描述符
        int fd;

    public:
        HttpResponse() : blank(LINE_END), status_code(0), fd(-1){}
        ~HttpResponse(){}
};

// 对端
// 读取、分析请求，构建响应
class EndPoint
{
    private:
        int sock;
        HttpRequest http_request;
        HttpResponse http_response;
        bool stop;
    
    private:
        bool RecvHttpRequestLine()
        {
            int ret = Util::ReadLine(sock, http_request.request_line);
            //assert(!http_request.request_line.empty());
            if(ret > 0)
            {
                http_request.request_line.pop_back();
            }
            else if(ret == 0)
            {
                // 对端关闭连接
                LOG(WARNING, "Endpoint close the connnection!");
                stop = true;
            }
            else 
            {
                // 读取错误
                stop = true;
            }
            //LOG(INFO, http_request.request_line);
            
            return stop;
        }

        bool RecvHttpRequestHeader()
        {
            std::string line;
            while(true)
            {
                line.clear();
                // 读取错误
                if(Util::ReadLine(sock, line) <= 0)
                {
                    LOG(ERROR, "read request_header error!");
                    stop = true;
                    break;
                }
                if(line != "\n") 
                {
                    line.pop_back(); // 除去\n 
                    //line.resize(line.size() - 1);
                    http_request.request_header.emplace_back(line);
                }
                else 
                {
                    http_request.blank = line;             
                    break;
                }
                //LOG(INFO, line);
            }
            return stop;
        }
        
        void ParseHttpRequestLine()
        {
            // 格式：Get URI HTTP/1.0
            // 使用stringstream来分割字符串，默认以空格为界限
            assert(!http_request.request_line.empty());
            std::stringstream ss(http_request.request_line);
            ss >> http_request.method >> http_request.uri >> http_request.version;
            
            // 将方法全部转换成大写
            std::string& method = http_request.method;
            std::transform(method.begin(), method.end(), method.begin(), ::toupper);
            //LOG(INFO, http_request.method);
            //LOG(INFO, http_request.uri);
            //LOG(INFO, http_request.version);
        }
    
        void ParseHttpRequestHeader()
        {
            std::string key;
            std::string value;
            
            for(auto &str : http_request.request_header)
            {
                if(Util::CutString(str, key, value, ": "))
                {
                    std::cout << "Debug: " << value << std::endl;
                    http_request.header_kv[key] = value;
                }
                else LOG(WARNING, "Cut Error");
            }
        }

        bool IsNeedRecvHttpBody()
        {
            std::string &method = http_request.method;
            if(method == "POST")
            {
                std::unordered_map<std::string, std::string> header_kv = http_request.header_kv;
                std::unordered_map<std::string, std::string>::iterator iter = header_kv.find("Content-Length");
                if(iter != header_kv.end())
                {
                    // 找到了
                    http_request.content_length = atoi(iter->second.c_str());
                    return true;
                }
                else
                {
                    // 没有找到
                    return false;
                }
            }

            return false;
        }

        bool RecvHttpRequestBody()
        {
            if(IsNeedRecvHttpBody())
            {
                int content_length = http_request.content_length;
                while(content_length)
                {
                    char ch;
                    ssize_t s = recv(sock, &ch, sizeof(ch), 0);
                    if(s > 0)
                    {
                        http_request.request_body += ch;
                        --content_length;
                    }
                    else if(s == 0)
                    {
                        stop = true;
                        break;
                    }
                    else 
                    {
                        // 读取失败
                        stop = true;
                        break;
                    }
                }
            }
            return stop;
        }

        int ProcessCgi()
        {
            // 实现CGI模式
            // 进程替换、进程创建、进程等待、进程间通信
            // 对于Cgi程序而言，需要处理HttpServer的数据，同时将结果返回给HttpServer，所以进入Cgi之前需要创建线程来指向该操作
            // 第一步：获取数据，数据存放在Get方法中的URL中（query_string）或者Post方法中的body中
            // 第二步：创建子进程，通过匿名管道进行进程间通信(这里站在父进程的角度)
            // 即：input表示父进程向子进程写入，output表示父进程向子进程读取
            std::string& bin = http_request.path; // 二进制文件路径
             
            // 第三步：子进程如何获得参数呢？
            // 对于POST方法中的body的参数，采用管道进行通信
            // 对于GET方法中的query_string，这里采用环境变量传输
            // 上述原因：POST方法中的参数一般比较多，所以采用管道进行传输，但是GET方法的参数比较少，使用管道写入代价比较大
            // 问题：子进程如何得知当前HTTP协议采用的是POST方法还是GET方法呢？
            // 通过环境变量告知子进程
            std::string& query_string = http_request.query_string;
            std::string& body_test = http_request.request_body;
            int content_length = http_request.content_length;
            // 环境变量都是KV格式
            std::string method = "METHOD=";
            std::string query_string_env = "QUERY_STRING=";
            std::string content_length_env = "CONTENT_LENGTH="; // Post方法。替换后子进程需要根据Content-Length进行读取
            // 构建响应
            std::string& response_body = http_response.response_body;
            // 退出码
            int code = 0;

            // 创建管道
            int input[2], output[2];
            if(pipe2(input, 0) == -1)
            {
                LOG(ERROR, "pipe input error!");
                code = 404;
                return code;
            }
            if(pipe2(output, 0) == -1)
            {
                LOG(ERROR, "pipe output error!");
                code = 404;
                return code;
            }

            int pid = fork();
            if(pid < 0)
            {
                // 表示子进程创建失败
                LOG(ERROR, "fork error!");
                code = 404;
                return code;
            }
            else if(pid == 0)
            {
                // 当前进程为子进程
                // 关闭input的读
                // 关闭output的写
                close(input[0]);
                close(output[1]);
                // exce*系列进行程序替换
                // 问题：进程替换后，子进程的代码会全部替换成目标文件代码，同时子进程读写管道的文件描述符也找不到了，那要怎么进行通信呢？
                // 解决：进程替换只会替换当前进程的代码，但是该进程在内核中的数据结构仍然存在。
                // 也就是说：子进程继承的父进程文件描述符表仍然存在
                // 特殊设计：将标准输入重定向到子进程的input中，将标准输出重定向到子进程的output中
                // 子进程视角
                // 1 -> input[1]
                // 0 -> output[0]
                if(dup2(input[1], 1) == -1 || dup2(output[0], 0) == -1)
                {
                    LOG(ERROR, "dup2 error!");
                    exit(2);
                }

                method += http_request.method;
                putenv((char*)method.c_str());
                if(http_request.method == "GET")
                {
                    query_string_env += query_string;
                    // 导入环境变量
                    putenv((char*)query_string_env.c_str());
                }
                else if(http_request.method == "POST")
                {
                   content_length_env += std::to_string(content_length);
                   putenv((char*)content_length_env.c_str());
                }

                // 进行替换
                execl(bin.c_str(), bin.c_str(), nullptr);
                // 替换失败
                //LOG(WARNING, "execl error!");
                code = BAD_REQUEST;
            }
            else 
            {
                // 当前进程为父进程
                close(input[1]);
                close(output[0]);

                if(http_request.method == "POST")
                {        
                    // 循环写入管道，因为管道的容量有限
                    int size = 0;
                    int total = 0;
                    const char* start = body_test.c_str();
                    size = write(output[1], start + total, body_test.size()- total);
                    while(total < content_length && (size = write(output[1], start + total, body_test.size() - total) > 0))
                    {
                        total += size;
                    } 
                }
                char ch;
                while(read(input[0], &ch, 1) > 0)
                {
                    response_body.push_back(ch);
                }
                //LOG(INFO, response_body);
                
                //std::cerr << "parent get query_string: " << response_body << std::endl;
                int mystatus = 0;
                int ret = waitpid(pid, &mystatus, 0);
                if(ret == pid)
                { 
                    if(WIFEXITED(mystatus))
                    {
                        if(WEXITSTATUS(mystatus) == 0)  code = OK;
                        else  code = 404;
                    }
                    else code = 404;

                }
                else 
                {
                    // 等待失败
                    LOG(WARNING, "wait error!");
                }
                close(input[0]);
                close(output[1]);
            }


            return code;
        }


        int ProcessNonCgi()
        {
            // 打开静态网页，将数据直接从网卡发出
            // sendfile可以直接从内核中发送数据，不需要拷贝到用户层(sendto)
            http_response.fd = open(http_request.path.c_str(), O_RDONLY);
            if(http_response.fd >= 0)
            {
                LOG(INFO, http_request.path + " open success!"); 
                return OK;
            }
            else 
            {
                return NOT_FOUND;
            }
        }

        void BuildOKResponse()
        {
            // 构建响应头部
            std::string line = "Content-Type: ";
            line += Suffix2Desc(http_request.suffix);
            line += LINE_END;
            http_response.response_header.push_back(line);

            line = "Content-Length: ";
            // CGI机制下，Content-Length为body的size
            if(http_request.cgi)
            {
                // 表示当前返回正文大小
                line += std::to_string(http_response.response_body.size());
            }
            else line += std::to_string(http_request.size); // 非CGI机制下，Content-Length为st中获取的st_size
            line += LINE_END;
            http_response.response_header.push_back(line);
        }

        void HandlerError(std::string path)
        {
            std::cout << "mypath===========:" << path << std::endl;
            // 将对应的CGI设置为false
            http_request.cgi = false;
            http_response.fd = open(path.c_str(), O_RDONLY, 0);
            if(http_response.fd > 0)
            {
                struct stat st;
                // 获取路径属性，这里的路径一定存在
                stat(path.c_str(), &st);
                http_request.size = st.st_size;

                std::string line = "Content-Type: text/html";
                line += LINE_END;
                http_response.response_header.push_back(line);

                line = "Content-Length: ";
                line +=  std::to_string(st.st_size);
                line += LINE_END;
                 http_response.response_header.push_back(line);
            }
            else LOG(WARNING, "open error_path failed!");
        } 

        void BuildHttpResponseHelper()
        {
            // 直接构建
            // 构建响应行
            std::string line = HTTP_VERSION;
            line += " ";
            line += std::to_string(http_response.status_code);
            line += " ";
            line += Code2Desc(http_response.status_code);
            line += LINE_END;
            http_response.response_line = line;

            // 构建Response的header，使用code进行判断
            int code = http_response.status_code;
            std::string path = WWW_ROOT;
            path += "/";
            switch(code)
            {
                case OK:
                  BuildOKResponse();
                  break;
                case NOT_FOUND:
                  path += PAGE_404;
                  HandlerError(path);
                    break;
                case BAD_REQUEST:
                    path += PAGE_401;
                  HandlerError(path);
                default:
                    break;
            }
        }
    
    public:
        bool IsStop()
        {
            return stop;
        }

        EndPoint(int _sock)
          :sock(_sock), stop(false){}
        
        void RecvHttpRequest()
        {
            if(!(RecvHttpRequestLine()) && !(RecvHttpRequestHeader())){
                ParseHttpRequestLine();
                ParseHttpRequestHeader();
                RecvHttpRequestBody();
            }
        }

        void BuildHttpResponse()
        {
            if(stop == true) return; 
            // 状态码设置
            int &code = http_response.status_code;
            // 查找尾缀位置
            size_t suffix_pos = -1;
            // GET：需要判断是否带参数（参数放在URL中）
            // POST：参数放在正文中
            // Post、POST大小写转换
            // 非法请求
            if(http_request.method != "GET" && http_request.method != "POST")
            {
                LOG(WARNING, http_request.method + "method error");
                // 暂时设置为404，后面在处理
                code = NOT_FOUND;
                goto END;
            }
            // 合法请求
            if(http_request.method == "GET")
            {
                size_t pos = http_request.uri.find("?");
                if(pos != std::string::npos)
                {
                    // 找到参数后
                    // 需要进行CGI处理
                    http_request.cgi = true;
                    Util::CutString(http_request.uri, http_request.path, http_request.query_string, "?");
                    std::cout << "PATH: " << http_request.path << std::endl;
                    std::cout << "QUERY_STRING: " << http_request.query_string << std::endl;
                }
                else
                {
                    // 没有找到
                    http_request.path = http_request.uri;
                    std::cout << "PATH: " << http_request.path << std::endl;
                }
            }
            else if(http_request.method == "POST")
            {
                // 有参数时
                // 需要进行CGI处理
                http_request.cgi = true;
                http_request.path = http_request.uri;
            }
            else
            {
                // 其他方法
            }

            http_request.path.insert(0, WWW_ROOT);
            std::cout << "Adjust Path: " << http_request.path << std::endl;
            // 路径调整
            if(http_request.path[http_request.path.size() - 1] == '/')
            {
                //需要调整为首页
                http_request.path += HOME_PAGE;
                std::cout << "Adjust Path: " << http_request.path << std::endl;
            }

            // 判断路径是否存在
            // stat返回值0：合法路径
            // stat返回值-1：非法路径
            struct stat st;
            if(stat(http_request.path.c_str(), &st) == 0)
            {
                // 表示路径存在
                // 存在路径访问的是一个目录、一个普通文件、可执行文件
                if(S_ISDIR(st.st_mode) == true)
                {
                    // 表示当前访问的是一个目录文件
                    // 因为无法确定路径下哪一个资源
                    // 所以一般Web服务器下每一个目录都有一个index.html
                    http_request.path += '/';
                    http_request.path += HOME_PAGE;
                    // 更新一下st属性，主要是为了修改size
                    stat(http_request.path.c_str(), &st);
                }
                // 判断当前文件是否具有可执行权限
                if((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
                {
                    // 具有可执行权限
                    // 特殊处理
                    http_request.cgi = true;
                }
                // 没有可执行权限，但是有参数，直接忽略参数
                else if(http_request.cgi == true)
                {
                    http_request.cgi = false;
                }
                http_request.size = st.st_size;
            }
            else
            {
                // 表示路径不存在
                // 注意：现在的路径为/a/b
                // 结尾为/的路径已经处理为加上HOME_PAGE
              std::cout << http_request.path << std::endl;
                LOG(INFO, http_request.path + " NOT_FOUND");
                code = NOT_FOUND;
                // 路径不存在时，直接返回静态网页404
                http_request.cgi = false;
                goto END;
            }

            // 说明当前资源找到了
            // 开始查询后缀
            // 从后向前找，找到的第一个"."为suffix位置
            suffix_pos = http_request.path.rfind(".");
            if(suffix_pos != std::string::npos)
            {
                // 找到后缀
                http_request.suffix = http_request.path.substr(suffix_pos);
            }
            else 
            {
                // 没有找到后缀
                // 默认为".html"
                http_request.suffix = ".html";
            }

            std::cout << "cgi: " << http_request.cgi << std::endl;
            // 开始处理
            if(http_request.cgi == true)
            {
                // CGI处理
                // 获取数据
                //std::string query_string;
                //if(http_request.method == "GET")
                //    query_string = http_request.query_string;
                //else query_string = http_request.request_body;

                code = ProcessCgi();
            }
            else
            {
                // 非CGI处理，返回静态网页
                http_response.status_code = ProcessNonCgi();
            }
END:

            BuildHttpResponseHelper();
            return ;

        }

        void SendHttpResponse()
        {
            // 发送响应行、响应报头、空行、正文
            send(sock, http_response.response_line.c_str(), http_response.response_line.size(), 0);
            for(auto &str : http_response.response_header)
            {
                send(sock, str.c_str(), str.size(), 0);
            }
            send(sock, http_response.blank.c_str(), http_response.blank.size(), 0);
            if(http_request.cgi)
            {
                std::string& body_test = http_response.response_body;
                size_t size = 0;
                size_t total = 0;
                const char* start = body_test.c_str();
                while(total < body_test.size() && ((size = send(sock, start + total, body_test.size() - total, 0)) > 0))
                {
                    total += size;
                }

            }
            else 
            {  
                // 根据之前的st中的size
                sendfile(sock, http_response.fd,  nullptr, http_request.size);
                // 关闭文件描述符
                close(http_response.fd);
                http_response.fd = -1;
            }
        }
        
        ~EndPoint()
        {
            close(sock);
        }

};

//#define DEBUG 1

// 回调方法：处理任务
class CallBack
{
    public:
      void operator()(int sock)
      {
          HandlerRequest(sock);
      }

      static void HandlerRequest(int _sock)
      {
          LOG(INFO, "Handler Requset Begin");
#ifdef DEBUG
          char buffer[4096];
          ssize_t s = recv(sock, buffer, sizeof(buffer), 0);
          //while(true)
          //{
          //    ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, SOCK_NONBLOCK);
          //    if(s > 0) break;
          //}
          std::cout << "-----------------------------begin---------------------------" << std::endl;
          std::cout << buffer << std::endl;
          std::cout << "------------------------------end----------------------------" << std::endl;
#else
          EndPoint* ep = new EndPoint(_sock);
          ep->RecvHttpRequest();
          if(!ep->IsStop())
          {
              ep->BuildHttpResponse();
              ep->SendHttpResponse();
          }
          delete ep;
#endif

          close(_sock);
          LOG(INFO, "Handler Request End!");
      }
};

#endif
